﻿using System;
using System.Linq;
using System.Net.Mail;
using System.Security.Cryptography;
using System.Text;
using System.Web.Mvc;
using System.ComponentModel;
using System.Collections;
using System.Text.RegularExpressions;
using System.Globalization;


namespace IndoReputation.Commons
{
    public static class CommonFunctions
    {
        /// <summary>
        /// Returns the first day of the week that the specified
        /// date is in using the current culture. 
        /// </summary>
        public static DateTime GetFirstDayOfWeek(DateTime dayInWeek)
        {
            var defaultCultureInfo = CultureInfo.CurrentCulture;
            return GetFirstDayOfWeek(dayInWeek, defaultCultureInfo);
        }

        /// <summary>
        /// Returns the first day of the week that the specified date 
        /// is in. 
        /// </summary>
        public static DateTime GetFirstDayOfWeek(DateTime dayInWeek, CultureInfo cultureInfo)
        {
            var firstDay = cultureInfo.DateTimeFormat.FirstDayOfWeek;
            var firstDayInWeek = dayInWeek.Date;
            while (firstDayInWeek.DayOfWeek != firstDay)
                firstDayInWeek = firstDayInWeek.AddDays(-1);
            return firstDayInWeek;
        }

        public static int CompareTrend(Int64 numFirst, Int64 numLater)
        {
            if (numFirst == 0)
            {
                return numLater == 0 ? 0 : 100;
            }
            var result = Convert.ToInt32((numLater * 100) / numFirst) - 100;
            return result;
        }

        public static string MD5Hash(string value)
        {
            return Convert.ToBase64String(new MD5CryptoServiceProvider().ComputeHash(new UTF8Encoding().GetBytes(value)));
        }

        public static SelectList ToSelectList<TEnum>(this TEnum enumObj)
        {
            var values = from TEnum e in Enum.GetValues(typeof(TEnum))
                         select new { Id = e, Name = GetEnumDescription(e) };
            return new SelectList(values, "Id", "Name", enumObj);
        }

        public static string GetEnumDescription<TEnum>(TEnum value)
        {
            var fi = value.GetType().GetField(value.ToString());
            var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
            return (attributes.Length > 0) ? attributes[0].Description : value.ToString();
        }

        public static T StringToEnum<T>(string name)
        {
            var names = Enum.GetNames(typeof(T));
            if (((IList)names).Contains(name))
            {
                return (T)Enum.Parse(typeof(T), name);
            }
            return default(T);
        }

        public static string IndentHtml(this string html)
        {
            return html.TrimHtmlWhiteSpace()
                       .IndentTags()
                       .WordWrap();
        }

        private static string TrimHtmlWhiteSpace(this string html)
        {
            return new Regex("(\\s|\t|\n|\r)+").Replace(html, " ");
        }

        private const int IndentSize = 4;
        private static int _indentLevel;

        private static readonly string NewLine = Environment.NewLine;
        private static readonly int NewLineLength = NewLine.Length;

        private const string BlockElements = "div|p|img|ul|ol|li|h[1-6]|blockquote";

        private static readonly Regex OpeningBlockRegex = new Regex("<(" + BlockElements + ")[^>]*>", RegexOptions.IgnoreCase | RegexOptions.Compiled);

        private static readonly Regex ClosingBlockRegex = new Regex("</(" + BlockElements + ")>", RegexOptions.IgnoreCase | RegexOptions.Compiled);

        private static readonly Regex EmptyTagRegex = new Regex("^<.*/>$", RegexOptions.Compiled);

        private static readonly Regex BrTagRegex = new Regex("^<br\\s*/>$", RegexOptions.Compiled);

        private const int WrapThreshold = 120;

        public static string WordWrap(this string html)
        {
            // wrap lines
            var lines = html.Split(new[] { NewLine }, StringSplitOptions.RemoveEmptyEntries);

            for (var i = 0; i < lines.Length; i++)
            {
                if (lines[i].Length <= WrapThreshold)
                    continue;

                var result = new StringBuilder();

                var currentLine = lines[i];

                var currentLineIndentSize = new Regex("^\\s+").Match(currentLine).Length;

                while (currentLine.Length > WrapThreshold)
                {
                    int splitPoint = currentLine.Substring(0, WrapThreshold - IndentSize).LastIndexOf(' ');

                    if (splitPoint < 0)
                        splitPoint = WrapThreshold; // cuts though code, though

                    result.Append(currentLine.Substring(0, splitPoint))
                          .Append(NewLine.PadRight(NewLineLength + currentLineIndentSize + IndentSize));

                    currentLine = currentLine.Substring(splitPoint + 1);
                }

                result.Append(currentLine);

                lines[i] = result.ToString();
            }

            return String.Join(NewLine, lines);
        }

        public static string IndentTags(this string html)
        {
            var indenting = true;
            html = new Regex("(<[\\w][\\w\\d]*[^>]*>)|(</[\\w][\\w\\d]*>)", RegexOptions.IgnoreCase).Replace(html,
                                                                                                             delegate(
                                                                                                                 Match m)
                                                                                                                 {
                                                                                                                     var
                                                                                                                         token
                                                                                                                             =
                                                                                                                             m
                                                                                                                                 .
                                                                                                                                 Groups
                                                                                                                                 [
                                                                                                                                     0
                                                                                                                                 ]
                                                                                                                                 .
                                                                                                                                 Value;
                                                                                                                     var
                                                                                                                         result
                                                                                                                             =
                                                                                                                             new StringBuilder
                                                                                                                                 ();
                                                                                                                     var
                                                                                                                         currentIndent
                                                                                                                             =
                                                                                                                             NewLine
                                                                                                                                 .
                                                                                                                                 PadRight
                                                                                                                                 (NewLineLength +
                                                                                                                                  IndentSize*
                                                                                                                                  _indentLevel);

                                                                                                                     if
                                                                                                                         (
                                                                                                                         OpeningBlockRegex
                                                                                                                             .
                                                                                                                             IsMatch
                                                                                                                             (token))
                                                                                                                     {
                                                                                                                         if
                                                                                                                             (
                                                                                                                             !indenting)
                                                                                                                             result
                                                                                                                                 .
                                                                                                                                 Append
                                                                                                                                 (currentIndent);

                                                                                                                         if
                                                                                                                             (
                                                                                                                             !EmptyTagRegex
                                                                                                                                  .
                                                                                                                                  IsMatch
                                                                                                                                  (token))
                                                                                                                             ++
                                                                                                                                 _indentLevel;

                                                                                                                         while
                                                                                                                             (
                                                                                                                             token
                                                                                                                                 .
                                                                                                                                 Length >
                                                                                                                             WrapThreshold)
                                                                                                                         {
                                                                                                                             int
                                                                                                                                 splitPoint
                                                                                                                                     =
                                                                                                                                     token
                                                                                                                                         .
                                                                                                                                         Substring
                                                                                                                                         (0,
                                                                                                                                          WrapThreshold)
                                                                                                                                         .
                                                                                                                                         LastIndexOf
                                                                                                                                         (' ');

                                                                                                                             result
                                                                                                                                 .
                                                                                                                                 Append
                                                                                                                                 (token
                                                                                                                                      .
                                                                                                                                      Substring
                                                                                                                                      (0,
                                                                                                                                       splitPoint))
                                                                                                                                 .
                                                                                                                                 Append
                                                                                                                                 (NewLine
                                                                                                                                      .
                                                                                                                                      PadRight
                                                                                                                                      (NewLineLength +
                                                                                                                                       IndentSize*
                                                                                                                                       (_indentLevel +
                                                                                                                                        1)));

                                                                                                                             token
                                                                                                                                 =
                                                                                                                                 token
                                                                                                                                     .
                                                                                                                                     Substring
                                                                                                                                     (splitPoint);
                                                                                                                         }

                                                                                                                         result
                                                                                                                             .
                                                                                                                             Append
                                                                                                                             (token)
                                                                                                                             .
                                                                                                                             Append
                                                                                                                             (NewLine
                                                                                                                                  .
                                                                                                                                  PadRight
                                                                                                                                  (NewLineLength +
                                                                                                                                   IndentSize*
                                                                                                                                   _indentLevel));

                                                                                                                         indenting
                                                                                                                             =
                                                                                                                             true;
                                                                                                                     }
                                                                                                                     else if
                                                                                                                         (
                                                                                                                         ClosingBlockRegex
                                                                                                                             .
                                                                                                                             IsMatch
                                                                                                                             (token))
                                                                                                                     {
                                                                                                                         result
                                                                                                                             .
                                                                                                                             Append
                                                                                                                             (NewLine
                                                                                                                                  .
                                                                                                                                  PadRight
                                                                                                                                  (NewLineLength +
                                                                                                                                   IndentSize*
                                                                                                                                   (--
                                                                                                                                    _indentLevel)))
                                                                                                                             .
                                                                                                                             Append
                                                                                                                             (token);
                                                                                                                         indenting
                                                                                                                             =
                                                                                                                             false;
                                                                                                                     }
                                                                                                                     else if
                                                                                                                         (
                                                                                                                         BrTagRegex
                                                                                                                             .
                                                                                                                             IsMatch
                                                                                                                             (token))
                                                                                                                     {
                                                                                                                         result
                                                                                                                             .
                                                                                                                             Append
                                                                                                                             (token)
                                                                                                                             .
                                                                                                                             Append
                                                                                                                             (NewLine
                                                                                                                                  .
                                                                                                                                  PadRight
                                                                                                                                  (NewLineLength +
                                                                                                                                   IndentSize*
                                                                                                                                   _indentLevel));
                                                                                                                         indenting
                                                                                                                             =
                                                                                                                             false;
                                                                                                                     }
                                                                                                                     else
                                                                                                                     {
                                                                                                                         result
                                                                                                                             .
                                                                                                                             Append
                                                                                                                             (token);
                                                                                                                         indenting
                                                                                                                             =
                                                                                                                             false;
                                                                                                                     }

                                                                                                                     return
                                                                                                                         result
                                                                                                                             .
                                                                                                                             ToString
                                                                                                                             ();
                                                                                                                 });
            return html;
        }

        public static bool SendEmail(string subject, string body, string from, string to)
        {
            var message = new MailMessage { From = new MailAddress(from) };
            message.To.Add(new MailAddress(to));
            message.IsBodyHtml = true;
            message.BodyEncoding = Encoding.UTF8;
            message.Subject = subject;
            message.Body = body;
            var client = new SmtpClient();
            try
            {
                client.Send(message);
                return true;
            }
            catch(Exception)
            {
                return false;
            }            
        }
        public static string GetRandomString()
        {
            var g = Guid.NewGuid();
            var guidString = Convert.ToBase64String(g.ToByteArray());
            guidString = guidString.Replace("=", "");
            guidString = guidString.Replace("+", "");
            return guidString;
        }
    }

    public class PlaceHolderAttribute : Attribute, IMetadataAware
    {
        private readonly string _placeholder;

        public PlaceHolderAttribute(string placeholder)
        {
            _placeholder = placeholder;
        }

        public void OnMetadataCreated(ModelMetadata metadata)
        {
            metadata.AdditionalValues["placeholder"] = _placeholder;
        }
    }

}